home *** CD-ROM | disk | FTP | other *** search
- /*==================================================================
- File: ZString.cpp
-
- Contains: Class that implements a generic container
- container for storing and manipulating
- string (textual) data.
-
- Written by: Eric Traut
-
- Copyright: 2000-2001 Connectix Corporation
-
- This source has been placed into the public domain by
- Connectix Corporation. You have the right to modify,
- distribute or use this code without any legal limitations
- or finanicial/licensing requirements. Connectix is not
- liable for any problems that result from the use of this
- code.
-
- If you have comments, feedback, questions, or would like
- to submit bug fixes or updates to this code, please email
- opensource@connectix.com.
- ==================================================================*/
-
- #include "ZString.h"
- #include "ZStringParser.h"
- #include "ZStringDictionary.h"
-
- #include <stdio.h>
-
- /*==================================================================
-
- EXPLANATION:
-
- A "ZString" is a class that encapsulates an immutable string
- in a cross-platform and language-neutral manner. They are used
- to solve problems involving cross-platform and multi-language
- text strings.
-
- The ZString class is also useful for encapsulating and
- manipulating strings in a cross-platform manner. It stores
- all strings internally as 8-bit characters. The code could
- be extended to support unicode in the future.
-
- A ZString is a very small object itself. It simply contains
- a pointer to a reference-counted structure that contains the
- actual string data. Assignment, therefore, is very simple.
- Other operations require the creation of new data blocks.
- All data blocks get cleaned up automatically when their
- reference counts reach zero again.
-
- A ZString can be constructed in two ways:
-
- ZString zString; // Creates an empty string
- or
- ZString zString("This is the string"); // Allocates space for copy of string
-
- Once created, the string can be assigned to or operated
- on in several ways. For example:
-
- zStringA += zStringB; // Concatenation
- zStringA = "Hello"; // Assignment
- zStringA = zStringB + " Dollars"; // Concatenation/Assignment
- zStringA.ReleaseData(); // Sets to empty string
- zStringA = ""; // Also sets to empty string
-
- Another method for setting the string data provides the full
- power of ZStrings. Using this method, the string gets a
- full name, as in the following example:
-
- zStringA.GetNamedString("<Z name=VPC/PrefDialog/WindowTitle>Preferences<\Z>");
-
- Note the HTLM/XML syntax. Because the string is given a name,
- it can be tracked and even replaced in an internal dictionary.
- This allows for easy localization of the strings, as an override
- library can be constructed that predefines all the program's
- strings in a different langauge.
-
- "Named ZStrings" require a simple runtime dictionary that
- tracks these strings as they are encountered in the program.
- Once one of these strings is encountered, a new ZString data
- block is created, and it is entered into the dictionary. Because
- the dictionary "references" that data block, its reference
- count will never go to zero until the entire dictionary is
- destroyed (at program termination time).
-
- The other powerful property of named ZStrings is that simple
- textual replacement can be done. Take the following example:
-
- zString.GetNamedString("<Z name=VPC/ResetDialog/MainText>"
- "Are you sure you want to reset &ldquo&replace00&rdquo&hellip"
- "<\Z>");
-
- This string contains a number of interesting pieces that get
- replaced before the string is entered into the dictionary.
- First, the &ldquo and &rdquo get turned into their
- appropriate "double curly quote" characters. Second, the &hellip
- is replaced by a true ellipsis. The &relace00 is left as-is within
- the string. It is a place-holder for programmatic replacement
- at a later time.
-
- The following metacharacters are defined. Their names are taken
- directly from the HTML 4.0 standard which uses ISO standard names
- for its character sets.
-
- Typography
- ----------
-   Non-breaking space
- &bull Bullet
- &ndash En dash
- &mdash Em dash
- <br> Line break
-
- Punctuation
- -----------
- ¡ Inverted exclamation point (Spanish)
- ¿ Inverted question mark
- &hellip Horizontal ellipsis
- &lsquo Left single quotation mark
- &rsquo Right single quotation mark
- &sbquo Single low-9 quotation mark
- &ldquo Left double quotation mark
- &rdquo Right double quotation mark
- &bdquo Double low-9 quotation mark
-
- Symbols
- -------
- ¢ Cent symbol (American)
- £ Pound sign (British)
- ¥ Yen symbol (Japanese)
- © Copyright symbol
- ® Registered trademark
- &trade Trade mark sign
- µ Micro sign (Greek mu)
- ¶ Paragraph symbol
- &pi Greek small letter pi
- & Ampersand
- < Less than
- > Greater than
-
- Foreign Characters
- ------------------
- À Capital A, grave accent
- Á Capital A, acute accent
- Â Capital A, circumflex accent
- Ã Capital A, tilde
- Ä Capital A, umlaut
- Å Capital A, ring
- Æ Capital AE ligature
- Ç Capital C, cedilla
- È Capital E, grave accent
- É Capital E, acute accent
- Ê Capital E, circumflex accent
- Ë Capital E, umlaut
- Ì Capital I, grave accent
- Í Capital I, acute accent
- Î Capital I, circumflex accent
- Ï Capital I, umlaut
- Ñ Capital N, tilde
- Ò Capital O, grave accent
- Ó Capital O, acute accent
- Ô Capital O, circumflex accent
- Õ Capital O, tilde
- Ö Capital O, umlaut
- Ø Capital O, slash
- Ù Capital U, grave accent
- Ú Capital U, acute accent
- Û Capital U, circumflex accent
- Ü Capital U, umlaut
- ß Small sz ligature, German
- à Lowercase a, grave accent
- á Lowercase a, acute accent
- â Lowercase a, circumflex accent
- ã Lowercase a, tilde
- ä Lowercase a, umlaut
- å Lowercase a, ring
- æ Lowercase ae ligature
- ç Lowercase c, cedilla
- è Lowercase e, grave accent
- é Lowercase e, acute accent
- ê Lowercase e, circumflex accent
- ë Lowercase e, umlaut
- ì Lowercase i, grave accent
- í Lowercase i, acute accent
- î Lowercase i, circumflex accent
- ï Lowercase i, umlaut
- ñ Lowercase n, tilde
- ò Lowercase o, grave accent
- ó Lowercase o, acute accent
- ô Lowercase o, circumflex accent
- õ Lowercase o, tilde
- ö Lowercase o, umlaut
- ø Lowercase o, slash
- ù Lowercase u, grave accent
- ú Lowercase u, acute accent
- û Lowercase u, circumflex accent
- ü Lowercase u, umlaut
- ÿ Lowercase y, umlaut
-
- The following metacharacters have been added for our own use:
-
- Replacement
- -----------
- &replaceNN Text replacement (NN is two-digit decimal number)
-
-
- Replacing parameters within a string is easy. Just use the
- following syntax:
-
- zStringA = zStringB.ReplaceParameter(0, "1,000");
- or
- zStringA = zStringB.ReplaceParameter(2, length == 1 ? "meter" : "meters");
-
- There are several rules you need to follow when creating a
- named ZString.
-
- 1. You must enclose them in a <Z> <\Z> pair of tags.
- 2. You must provide a "name" parameter in the <Z> tag.
- 3. The actual string must consist of only 7-bit ASCII
- characters. For more complex characters, use the
- above metacharacters. The only 7-bit characters you
- can't encode directly are the ampersand, less than
- or greater than. To encode these characters, use
- the "&", "<", and ">" metacharacters.
- 4. You can't create ZStrings at static initialization
- time. The registration dictionary is not yet created
- and the override library hasn't yet been loaded at
- this time.
-
- ==================================================================*/
-
-
-
- /*------------------------------------------------------------------
- ZString
- ------------------------------------------------------------------*/
-
- ZString::ZString(
- const char * inString)
- : mData(NULL)
- {
- Z_UInt32 stringLength = strlen(inString);
-
- if (stringLength != 0)
- {
- AllocateData(stringLength);
- FillInString(inString, 0, stringLength);
- }
- }
-
-
- /*------------------------------------------------------------------
- GetNamedString
-
- This method looks up the specified named string in the
- dictionary, creating a new dictionary entry if it's not
- already present. The actual string data is stored in the
- object.
-
- The inDataIsVolatile should be true if the name string
- may go away or move throughout the execution of the program.
- In most environments, strings are stored as const arrays in
- the binary, so we can just store a pointer to them within the
- dictionary. However, if they are going to move, we need
- to allocate additional space and make a copy.
- ------------------------------------------------------------------*/
-
- void
- ZString::GetNamedString(
- const char * inNamedString,
- Z_Boolean inDataIsVolatile)
- {
- ZStringParser & parser = ZStringParser::GetZStringParser();
- ZStringDictionary & dictionary = ZStringDictionary::GetZStringDictionary();
- ZStringParseInfo parseInfo;
-
- if (parser.ParseNamedString(inNamedString, parseInfo, inDataIsVolatile))
- {
- if (!dictionary.LookUpString(parseInfo, *this))
- parser.CreateNewZString(parseInfo, *this);
- }
- }
-
-
- /*------------------------------------------------------------------
- GetNamedZString [static]
-
- This method is a static form of GetNameString and returns a
- newly created ZString object.
- ------------------------------------------------------------------*/
-
- ZString
- ZString::GetNamedZString(
- const char * inNamedString,
- Z_Boolean inDataIsVolatile)
- {
- ZString newString;
-
- newString.GetNamedString(inNamedString, inDataIsVolatile);
-
- return newString;
- }
-
-
- /*------------------------------------------------------------------
- PopulateDictionary [static]
- ------------------------------------------------------------------*/
-
- void
- ZString::PopulateDictionary(
- const char * inOverrideDict,
- Z_Boolean inDataIsVolatile)
- {
- ZStringParser & parser = ZStringParser::GetZStringParser();
- ZStringDictionary & dictionary = ZStringDictionary::GetZStringDictionary();
- ZStringParseInfo parseInfo;
- const char * curDictPtr = inOverrideDict;
-
- while (*curDictPtr != '\0')
- {
- if (parser.ParseNamedString(curDictPtr, parseInfo, inDataIsVolatile))
- {
- ZString tempZString;
- if (!dictionary.LookUpString(parseInfo, tempZString))
- parser.CreateNewZString(parseInfo, tempZString);
- }
-
- // Move on to the next override value.
- curDictPtr += parseInfo.fNamedStringLimit - parseInfo.fNamedStringStart + 1;
- }
- }
-
-
- /*------------------------------------------------------------------
- AllocateData
- ------------------------------------------------------------------*/
-
- void
- ZString::AllocateData(
- Z_UInt32 inStringLength)
- {
- // Release the old data.
- ReleaseData();
-
- mData = ZStringData::Allocate(inStringLength);
- if (mData != NULL)
- mData->IncrementRefCount();
- }
-
-
- /*------------------------------------------------------------------
- GetCString
- ------------------------------------------------------------------*/
-
- Z_UInt16
- ZString::GetCString(
- char * outString,
- Z_UInt16 inBufferSize)
- {
- Z_UInt32 cStrLen;
-
- check(inBufferSize > 0);
-
- if (mData == NULL)
- {
- cStrLen = 0;
- }
- else
- {
- cStrLen = mData->GetLength();
- if (cStrLen > inBufferSize - 1)
- cStrLen = inBufferSize - 1;
-
- memcpy(outString, mData->GetStringData(), cStrLen);
- outString[cStrLen] = 0;
- }
-
- check(cStrLen == (Z_UInt16)cStrLen);
-
- return (Z_UInt16)cStrLen;
- }
-
-
- /*------------------------------------------------------------------
- GetPString
- ------------------------------------------------------------------*/
-
- Z_UInt8
- ZString::GetPString(
- Z_UInt8 * outString,
- Z_UInt16 inBufferSize)
- {
- Z_UInt32 pStrLen;
-
- check(inBufferSize > 0);
-
- // Pascal strings can be at most 255 characters.
- // It doesn't make sense for the buffer size to be more.
- check(inBufferSize <= 256);
- if (inBufferSize > 256)
- inBufferSize = 256;
-
- if (mData == NULL)
- {
- pStrLen = 0;
- }
- else
- {
- pStrLen = mData->GetLength();
- if (pStrLen > inBufferSize - 1)
- pStrLen = inBufferSize - 1;
-
- memcpy(&outString[1], mData->GetStringData(), pStrLen);
- }
-
- outString[0] = pStrLen;
- return (Z_UInt8)pStrLen;
- }
-
-
- /*------------------------------------------------------------------
- GetSubstring
-
- Scans the string for substrings separated by a specified
- escape character. The index is zero-based.
- ------------------------------------------------------------------*/
-
- ZString
- ZString::GetSubstring(
- Z_UInt16 inIndex,
- char inSeparator)
- {
- ZString subString;
- Z_UInt16 subStringsFound = 0;
- Z_UInt32 totalLength = GetLength();
- Z_UInt32 charsLeft = totalLength;
- const char * scanPtr;
-
- if (mData != NULL)
- {
- scanPtr = mData->GetStringData();
-
- while (true)
- {
- // Have we found the start of the specified string?
- if (subStringsFound == inIndex)
- {
- const char * startOfSubString = scanPtr;
-
- // Scan ahead for the end of the string or the
- // next separator.
- while (charsLeft > 0 && *scanPtr != inSeparator)
- {
- scanPtr++;
- charsLeft--;
- }
-
- // Set the string.
- if (scanPtr != startOfSubString)
- subString.SetString(startOfSubString, scanPtr - startOfSubString);
-
- break;
- }
-
- if (*scanPtr == inSeparator)
- subStringsFound++;
-
- scanPtr++;
- charsLeft--;
-
- if (charsLeft == 0)
- break;
- }
- }
-
- return subString;
- }
-
-
- /*------------------------------------------------------------------
- SetString
- ------------------------------------------------------------------*/
-
- void
- ZString::SetString(
- const char * inString,
- Z_UInt16 inLength)
- {
- if (inLength == 0)
- {
- ReleaseData();
- }
- else
- {
- AllocateData(inLength);
- FillInString(inString, 0, inLength);
- }
- }
-
-
- /*------------------------------------------------------------------
- SetCString
- ------------------------------------------------------------------*/
-
- void
- ZString::SetCString(
- const char * inString)
- {
- SetString(inString, strlen(inString));
- }
-
-
- /*------------------------------------------------------------------
- SetPString
- ------------------------------------------------------------------*/
-
- void
- ZString::SetPString(
- const Z_UInt8 * inString)
- {
- SetString((const char *)inString + 1, inString[0]);
- }
-
-
- /*------------------------------------------------------------------
- operator =
- ------------------------------------------------------------------*/
-
- ZString
- ZString::operator = (
- ZString & inString)
- {
- ReleaseData();
- mData = inString.mData;
- if (mData != NULL)
- mData->IncrementRefCount();
-
- return *this;
- }
-
-
- /*------------------------------------------------------------------
- ConcatStrings
- ------------------------------------------------------------------*/
-
- void
- ZString::ConcatStrings(
- const char * inStringA,
- Z_UInt16 inStringALen,
- const char * inStringB,
- Z_UInt16 inStringBLen)
- {
- AllocateData(inStringALen + inStringBLen);
- FillInString(inStringA, 0, inStringALen);
- FillInString(inStringB, inStringALen, inStringBLen);
- }
-
-
- /*------------------------------------------------------------------
- ConcatString
- ------------------------------------------------------------------*/
-
- void
- ZString::ConcatString(
- const char * inString,
- Z_UInt16 inStringLen)
- {
- ZStringData * oldData = mData;
- Z_UInt16 oldLength = GetLength();
-
- // This is a little gross. We're going to "unhook" the
- // data from this object temporarily. This allows us
- // to allocate a new block without deallocating the
- // existing one.
- mData = NULL;
-
- AllocateData(oldLength + inStringLen);
- if (oldData != NULL)
- FillInString(oldData->GetStringData(), 0, oldLength);
- FillInString(inString, oldLength, inStringLen);
-
- // Now, decrement the ref count on the old data.
- // If no one else is accessing it, it will dispose
- // of itself.
- if (oldData != NULL)
- oldData->DecrementRefCount();
- }
-
-
- /*------------------------------------------------------------------
- ReplaceParameter
- ------------------------------------------------------------------*/
-
- ZString
- ZString::ReplaceParameter(
- Z_UInt8 inParamNum,
- const char * inString)
- {
- // We only support decimal numbers from 00 to 99.
- check(inParamNum < 100);
-
- if (mData != NULL)
- {
- char replaceString[32];
- const char * replMetaChar;
-
- sprintf(replaceString, "&replace%02d", inParamNum);
-
- // Look for the replacement string.
- replMetaChar = strstr(mData->GetStringData(), replaceString);
- check(replMetaChar != NULL);
-
- // If we didn't find it, don't replace anything.
- if (replMetaChar != NULL)
- {
- // Calculate the new string size.
- Z_UInt16 replaceTextLen = strlen(inString);
- Z_UInt16 newStringLen = mData->GetLength() - 10 + replaceTextLen;
-
- ZStringData * oldData = mData;
-
- // We're going to "unhook" the data from this object
- // temporarily. This allows us to allocate a new block
- // without deallocating the existing one.
- mData = NULL;
-
- AllocateData(newStringLen);
-
- Z_UInt16 firstRunLen = replMetaChar - oldData->GetStringData();
-
- // Copy the portion before the metacharacter.
- if (firstRunLen != 0)
- FillInString(oldData->GetStringData(), 0, firstRunLen);
-
- // Copy the text we're inserting.
- if (replaceTextLen != 0)
- FillInString(inString, firstRunLen, replaceTextLen);
-
- // Copy the remainder.
- if (firstRunLen + replaceTextLen < newStringLen)
- FillInString(replMetaChar + 10, firstRunLen + replaceTextLen, newStringLen - firstRunLen - replaceTextLen);
-
- // Now, decrement the ref count on the old data.
- // If no one else is accessing it, it will dispose
- // of itself.
- oldData->DecrementRefCount();
- }
- }
-
- return *this;
- }
-
-
- /*------------------------------------------------------------------
- ReplaceParameter
- ------------------------------------------------------------------*/
-
- ZString
- ZString::ReplaceParameter(
- Z_UInt8 inParamNum,
- Z_SInt32 inNumber)
- {
- char stringBuf[16];
- sprintf(stringBuf, "%d", inNumber);
-
- return ReplaceParameter(inParamNum, stringBuf);
- }
-
-
-